#include "Plane.h"
#include "Math.h"

Plane::Plane()
{
	mNormal = glm::vec3(0.0f, 1.0f, 0.0f);
	mDistance = 0.0f;
}

Plane::Plane(const glm::vec3& normal, float distance)
{
	mNormal = glm::normalize(normal);
	mDistance = distance;
}

Plane::Plane(const glm::vec3& point, const glm::vec3& normal)
{
	mNormal = glm::normalize(normal);
	mDistance = glm::dot(normal, point);
}

Plane::Plane(const glm::vec3& point1, const glm::vec3& point2, const glm::vec3& point3)
{
	glm::vec3 edge1 = point2 - point1;
	glm::vec3 edge2 = point3 - point1;
	mNormal = glm::normalize(glm::cross(edge1, edge2));
	mDistance = glm::dot(mNormal, point1);
}

Plane::Plane(const glm::vec4& plane)
{
	mNormal = glm::vec3(plane.x, plane.y, plane.z);
	mDistance = plane.w;
}

Plane::Plane(float a, float b, float c, float d)
{
	mNormal = glm::vec3(a, b, c);
	mDistance = d;
}

Plane::~Plane()
{
}

void Plane::Set(const glm::vec3& normal, float distance)
{
	mNormal = normal;
	mDistance = distance;
}

void Plane::Set(const glm::vec3& point, const glm::vec3& normal)
{
	mNormal = normal;
	mDistance = glm::dot(mNormal, point);
}

void Plane::Set(const glm::vec3& point1, const glm::vec3& point2, const glm::vec3& point3)
{
	glm::vec3 vec1 = point2 - point1;
	glm::vec3 vec2 = point3 - point1;
	mNormal = glm::cross(vec1, vec2);
	mNormal = glm::normalize(mNormal);
	mDistance = glm::dot(mNormal, point1);
}

void Plane::Set(const glm::vec4& plane)
{
	mNormal = glm::vec3(plane.x, plane.y, plane.z);
	mDistance = plane.w;
}

void Plane::Normalise()
{
	float magnitude = glm::length(mNormal);
	mNormal /= magnitude;
	mDistance /= magnitude;
}

float Plane::Distance(const glm::vec3& point) const
{
	return glm::dot(point, mNormal) + mDistance;
}

float Plane::Distance(const glm::vec4& point) const
{
	return glm::dot(glm::vec3(point), mNormal) + mDistance;
}

bool Plane::IsPointOnPlane(const glm::vec3& point) const
{
	return Distance(point) >= -0.0001f;
}

bool Plane::IsPointOnPlane(const glm::vec4& point) const
{
	return Distance(point) >= -Math::M_EPSILON;
}

void Plane::SetNormal(const glm::vec3& normal)
{
	mNormal = normal;
}

void Plane::SetDistance(float distance)
{
	mDistance = distance;
}

void Plane::Transform(const glm::mat4& matrix)
{
	glm::vec4 plane = glm::vec4(mNormal, mDistance);
	plane = matrix * plane;
	mNormal = glm::vec3(plane.x, plane.y, plane.z);
	mDistance = plane.w;
}

Plane Plane::Transformed(const glm::mat4& matrix) const
{
	glm::vec4 plane = glm::vec4(mNormal, mDistance);
	plane = matrix * plane;
	return Plane(glm::vec3(plane.x, plane.y, plane.z), plane.w);
}
